¥Script loading strategies
在正确的时间加载脚本涉及许多问题。没有什么事情像看上去那么简单!一个常见的问题是页面上的所有 HTML 都是按照其出现的顺序加载的。如果你使用 JavaScript 来操作页面上的元素(或更准确地说,是 文档对象模型),那么如果 JavaScript 在你尝试执行操作的 HTML 之前加载和解析,你的代码将无法工作。
¥There are a number of issues involved with getting scripts to load at the right time. Nothing is as simple as it seems!A common problem is that all the HTML on a page is loaded in the order in which it appears.If you are using JavaScript to manipulate elements on the page (or more accurately, the Document Object Model), your code won't work if the JavaScript is loaded and parsed before the HTML you are trying to do something to.
在上面的代码示例中,在内部和外部示例中,JavaScript 在解析 HTML 主体之前在文档的头部加载并运行。这可能会导致错误,因此我们使用了一些构造来解决它。
¥In the above code examples, in the internal and external examples the JavaScript is loaded and run in the head of the document, before the HTML body is parsed.This could cause an error, so we've used some constructs to get around it.
在内部示例中,你可以看到代码周围的结构:
¥In the internal example, you can see this structure around the code:
jsdocument.addEventListener("DOMContentLoaded", () => { // …});这是一个事件监听器,监听浏览器的 DOMContentLoaded 事件,该事件表示 HTML body 已完全加载并解析。该块内的 JavaScript 直到该事件被触发后才会运行,因此可以避免错误(你将在课程的后面部分了解 了解事件)。
¥This is an event listener, which listens for the browser's DOMContentLoaded event, which signifies that the HTML body is completely loaded and parsed.The JavaScript inside this block will not run until after that event is fired, therefore the error is avoided (you'll learn about events later in the course).
在外部示例中,我们使用更现代的 JavaScript 功能来解决问题,即 defer 属性,它告诉浏览器在到达 标记元素后继续下载 HTML 内容。
¥In the external example, we use a more modern JavaScript feature to solve the problem, the defer attribute, which tells the browser to continue downloading the HTML content once the tag element has been reached.
html在这种情况下,脚本和 HTML 将同时加载,并且代码将正常工作。
¥In this case both the script and the HTML will load simultaneously and the code will work.
注意:在外部情况下,我们不需要使用 DOMContentLoaded 事件,因为 defer 属性为我们解决了这个问题。我们没有对内部 JavaScript 示例使用 defer 解决方案,因为 defer 仅适用于外部脚本。
¥Note: In the external case, we did not need to use the DOMContentLoaded event because the defer attribute solved the problem for us.We didn't use the defer solution for the internal JavaScript example because defer only works for external scripts.
解决此问题的一种老式解决方案是将脚本元素放在正文的底部(例如,在 标记之前),以便在解析所有 HTML 后加载它。此解决方案的问题在于,在加载 HTML DOM 之前,脚本的加载/解析会被完全阻止。在包含大量 JavaScript 的大型网站上,这可能会导致严重的性能问题,从而降低网站速度。
¥An old-fashioned solution to this problem used to be to put your script element right at the bottom of the body (e.g. just before the tag), so that it would load after all the HTML has been parsed.The problem with this solution is that loading/parsing of the script is completely blocked until the HTML DOM has been loaded.On larger sites with lots of JavaScript, this can cause a major performance issue, slowing down your site.
异步和延迟¥async and defer
实际上,我们可以使用两个现代功能来绕过阻塞脚本的问题 - async 和 defer(我们在上面看到)。我们来看看这两者之间的区别。
¥There are actually two modern features we can use to bypass the problem of the blocking script — async and defer (which we saw above).Let's look at the difference between these two.
使用 async 属性加载的脚本将下载脚本,而不会在获取脚本时阻塞页面。但是,一旦下载完成,脚本就会执行,这会阻止页面呈现。这意味着在脚本执行完成之前,将阻止处理网页上的其余内容并向用户显示。你无法保证脚本将以任何特定顺序运行。当页面中的脚本彼此独立运行且不依赖于页面上的其他脚本时,最好使用 async。
¥Scripts loaded using the async attribute will download the script without blocking the page while the script is being fetched.However, once the download is complete, the script will execute, which blocks the page from rendering. This means that the rest of the content on the web page is prevented from being processed and displayed to the user until the script finishes executing.You get no guarantee that scripts will run in any specific order.It is best to use async when the scripts in the page run independently from each other and depend on no other script on the page.
使用 defer 属性加载的脚本将按照它们在页面上出现的顺序加载。它们只有在页面内容全部加载后才会运行,如果你的脚本依赖于就位的 DOM(例如,它们修改页面上的一个或多个元素),这将非常有用。
¥Scripts loaded with the defer attribute will load in the order they appear on the page.They won't run until the page content has all loaded, which is useful if your scripts depend on the DOM being in place (e.g. they modify one or more elements on the page).
以下是不同脚本加载方法的直观表示以及这对你的页面意味着什么:
¥Here is a visual representation of the different script loading methods and what that means for your page:
该图片来自 HTML 规范,根据 CC BY 4.0 许可条款复制并裁剪为缩小版本。
¥This image is from the HTML spec, copied and cropped to a reduced version, under CC BY 4.0 license terms.
例如,如果你有以下脚本元素:
¥For example, if you have the following script elements:
html你不能依赖脚本加载的顺序。jquery.js 可能在 script2.js 和 script3.js 之前或之后加载,如果是这种情况,这些脚本中依赖于 jquery 的任何函数都将产生错误,因为在脚本运行时不会定义 jquery。
¥You can't rely on the order the scripts will load in.jquery.js may load before or after script2.js and script3.js and if this is the case, any functions in those scripts depending on jquery will produce an error because jquery will not be defined at the time the script runs.
当你有一堆后台脚本需要加载,并且你只想尽快将它们安装到位时,应该使用 async。例如,也许你有一些游戏数据文件需要加载,游戏实际开始时需要这些数据文件,但现在你只想继续显示游戏简介、标题和大厅,而不会被脚本加载阻止 。
¥async should be used when you have a bunch of background scripts to load in, and you just want to get them in place as soon as possible.For example, maybe you have some game data files to load, which will be needed when the game actually begins, but for now you just want to get on with showing the game intro, titles, and lobby, without them being blocked by script loading.
使用 defer 属性加载的脚本(见下文)将按照它们在页面中出现的顺序运行,并在脚本和内容下载后立即执行它们:
¥Scripts loaded using the defer attribute (see below) will run in the order they appear in the page and execute them as soon as the script and content are downloaded:
html在第二个示例中,我们可以确定 jquery.js 将在 script2.js 和 script3.js 之前加载,并且 script2.js 将在 script3.js 之前加载。它们只有在页面内容全部加载后才会运行,如果你的脚本依赖于就位的 DOM(例如,它们修改页面上的一个或多个元素),这将非常有用。
¥In the second example, we can be sure that jquery.js will load before script2.js and script3.js and that script2.js will load before script3.js.They won't run until the page content has all loaded, which is useful if your scripts depend on the DOM being in place (e.g. they modify one or more elements on the page).
总结一下:
¥To summarize:
async 和 defer 都指示浏览器在单独的线程中下载脚本,而页面的其余部分(DOM 等)正在下载,因此在获取过程中页面加载不会被阻止。下载完成后,具有 async 属性的脚本将立即执行。这会阻塞页面并且不保证任何特定的执行顺序。具有 defer 属性的脚本将按照它们所在的顺序加载,并且只有在所有内容加载完成后才会执行。如果你的脚本应立即运行并且它们没有任何依赖,则使用 async。如果你的脚本需要等待解析并依赖于其他脚本和/或 DOM,请使用 defer 加载它们,并按照你希望浏览器执行它们的顺序放置其相应的 元素。